from django.shortcuts import render, get_object_or_404
from django.views.generic import ListView
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.db.models import Count
from . models import Post, Comment
from . forms import EmailPostForm, CommentForm
from taggit.models import Tag


def post_list(request, tag_slug=None):
    object_list = Post.published.all()
    tag = None

    if tag_slug:
        tag = get_object_or_404(Tag, slug=tag_slug)
        #过滤所有帖子只保留给定标签的帖子,因为有一个多对多（many-to-many）的关系，
        #必须通过给定的标签列表来过滤，标签列表只包含一个元素。'''
        object_list = object_list.filter(tags__in=[tag])

    #posts = Post.published.all()
    paginator = Paginator(object_list, 3) # 设置页面显示数量为3
    page = request.GET.get('page')   # page GET 参数来指明页数
    try:
        posts = paginator.page(page)  #通过调用Paginator的 page()方法在期望的页面中获得了对象
    except PageNotAnInteger:
        # 只有一页就显示第一页
        posts = paginator.page(1)
    except EmptyPage:
        # 最后一页就显示最后一页结果
        posts = paginator.page(paginator.num_pages)
    return render(request,
                    'blog/post/list.html',
                    {'page': page, 'posts': posts, 'tag': tag})


class PostListView(ListView):
    queryset = Post.published.all()
    context_object_name = 'posts'
    paginate_by = 3
    template_name = 'blog/post/list.html'


# 使用 post_detail 来显示帖子内容和评论
def post_detail(request, year, month, day, post):
    post = get_object_or_404(Post, slug=post,
                                    status='published',
                                    publish__year=year,
                                    publish__month=month,
                                    publish__day=day)
    # 添加 查询集（QuerySet）列出帖子的有效评论
    comments = post.comments.filter(active=True)
    new_comment = None #这行原文里没有，但是测试发现必须添加

    if request.method != 'POST':
        # GET 请求加载表单
        comment_form = CommentForm()
    else:
        # POST 请求提交数据
        comment_form = CommentForm(data=request.POST)
        #验证数据去实例化表单
        if comment_form.is_valid():
            # 填写评论表单但不提交，保存在内存中；
            #调用这个表单的save()方法创建一个新的Comment对象
            new_comment = comment_form.save(commit=False)
            # 为评论分配给定的帖子
            new_comment.post = post
            # Save()方法创建了一个表单链接的model的实例，并将评论写入数据库
            new_comment.save()

    #列出相似的帖子
    #返回一个包含当前帖子所有标签的ID的Python列表
    post_tags_ids = post.tags.values_list('id', flat=True)
    #获取所有包含这些标签的帖子排除当前的帖子
    similar_posts = Post.published.filter(tags__in=post_tags_ids).exclude(id=post.id)
    '''使用Count聚合函数来生成一个计算字段same_tags，该字段包含与查询到的所有标签共享的标签数量;
       通过共享的标签数量来排序（降序）结果并且通过publish字段来挑选拥有相同共享标签数量的帖子中的最近的一篇帖子。
       我们对返回的结果进行切片只保留最前面的4篇帖子'''
    similar_posts = similar_posts.annotate(same_tags=Count('tags'))\
                                .order_by('-same_tags','-publish')[:4]

    return render(request,
                    'blog/post/detail.html',
                    {'post': post,
                    'comments': comments,
                    'new_comment': new_comment,
                    'comment_form': comment_form,
                    'similar_posts': similar_posts})


def post_share(request, post_id):
    '''分享帖子'''
    # retrieve post by id
    post = get_object_or_404(Post, id=post_id, status='published')
    sent = False

    if request.method !='POST':
        #获取表单
        form = EmailPostForm()
    else:
        # 提交表单数据
        form = EmailPostForm(request.POST)
        #验证表单
        if form.is_valid():
            #获取验证通过的数据
            cd = form.cleaned_data  #保存验证通过的数据
            post_url = request.build_absolute_uri(post.get_absolute_url())
            #设置 subject 来存储一条信息，信息内容通过使用字符串格式化将新建表单的name, email地址，帖子主题传到消息中
            subject = '{} ({}) recommends you reading "()"'.format(cd['name'], cd['email'], post.title)
            #设置变量 message 存储信息，使用格式化字符串，将表单的主题， url 名字，以及新建表单内容（表单 comments 属性）
            message = 'Read "{}" at {}\n\n{}\'s comments: {}'.format(post.title, post_url, cd['name'], cd['comments'])
            #send_mail(subject, message, [cd['email']], [cd['to']])
            sent = True
    return render(request,
                    'blog/post/share.html',
                    {'post': post,
                    'form': form,
                    'sent':sent})
